package com.lsj.securityhanlder.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.*;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.io.PrintWriter;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("lsj")
                .password("123456")
                .roles("admin");
    }

    /*在前后端分离这样的开发架构下，前后端的交互都是通过 JSON 来进行，无论登录成功还是失败，都不会有什么服务端跳转或者客户端跳转之类。
      登录成功了，服务端就返回一段登录成功的提示 JSON 给前端，前端收到之后，该跳转该展示，由前端自己决定，就和后端没有关系了。
      登录失败了，服务端就返回一段登录失败的提示 JSON 给前端，前端收到之后，该跳转该展示，由前端自己决定，也和后端没有关系了。*/

    /*使用successHandler、failHandler、logoutHandler和authenticationEntryPoint实现前后端分离，该方案是基于session的认证，如果前后端分离只是网页+服务端，基于session既方便又省事
    思路：以上方法都含有request、response入参，通过修改contentType，返回"application/json;charset=utf-8"格式的数据，实现前后端分离
    * successHandler：登录成功后，将人员信息以json格式返回给前端
    * failHandler：登录失败，将异常信息以json格式返回给前台，可以通过判断异常类型，返回跟准确的登录失败的信息提示
    * logoutHandler：注销成功后，将注销成功的信息以json格式返回给前台
    * authenticationEntryPoint：用来解决匿名用户访问无权限资源时的异常,非前后端分离项目，未登录时访问资源，回跳到前台登录页面(即默认的authenticationEntryPoint)，
    *                           但是在前后端分离项目时，需要返回提示信息，该接口实现未认证时访问资源返回前台的信息*/
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginProcessingUrl("/doLogin")
                .successHandler((request, response, authentication) -> {
                    Object principal = authentication.getPrincipal();
                    response.setContentType("application/json;charset=utf-8");
                    PrintWriter writer = response.getWriter();
                    writer.write(new ObjectMapper().writeValueAsString(principal));
                    writer.flush();
                    writer.close();
                })
                .usernameParameter("name")
                .passwordParameter("passwd")
                .failureHandler((request, response, e) -> {
                    response.setContentType("application/json;charset=utf-8");
                    PrintWriter writer = response.getWriter();
                    String msg = null;
                    if (e instanceof LockedException) {
                        msg = "账户被锁定，请联系管理员!";
                    } else if (e instanceof CredentialsExpiredException) {
                        msg = "密码过期，请联系管理员!";
                    } else if (e instanceof AccountExpiredException) {
                        msg = "账户过期，请联系管理员!";
                    } else if (e instanceof DisabledException) {
                        msg = "账户被禁用，请联系管理员!";
                    } else if (e instanceof BadCredentialsException) {
                        msg = "用户名或者密码输入错误，请重新输入!";
                    }
                    writer.write(msg);
                    writer.flush();
                    writer.close();
                })
                .permitAll()
                .and()
                .logout()
                .logoutSuccessHandler((request, response, authentication) -> {
                    response.setContentType("application/json;charset=utf-8");
                    PrintWriter writer = response.getWriter();
                    writer.write("注销成功");
                    writer.flush();
                    writer.close();
                })
                .permitAll()
                .and()
                .csrf().disable()
                .exceptionHandling()
                .authenticationEntryPoint((request, response, authException) -> {
                    response.setContentType("application/json;charset=utf-8");
                    PrintWriter writer = response.getWriter();
                    writer.write("尚未登录，请先登录");
                    writer.flush();
                    writer.close();
                });

    }
}
